在接下來的的幾天內,需要跟 API server 去整合跟修正使用真實資料後的調整,在這邊會使用到了 React Hook
來整合我們的 Request API 的資料流。
首先,在一般的 API Request 有著重複的使用場景:
API request
的執行狀態。Request
執行期間的任何錯誤。Body
所以我先建立了一個 hook useApiRequest
import {useCallback, useState} from "react";
export default function useApiRequest(apiFunction) {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [responseData, setResponseData] = useState(null);
const fetchAPI = useCallback(
async (...args) => {
try {
setIsLoading(true);
const result = await apiFunction(...args);
setResponseData(result);
setIsLoading(false);
return result;
} catch (e) {
setError(e);
setIsLoading(false);
throw e;
}
},
[apiFunction]
);
return {isLoading, fetchAPI, responseData, error };
};
import axios from 'axios';
import {useAuthContext} from "@/context/AuthContext";
const apiClient = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_HOST
});
apiClient.interceptors.request.use(async (config) => {
const {user} = useAuthContext();
if (user !== null) {
const token = await user.getIdToken();
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, (error) => {
return Promise.reject(error);
});
export default apiClient;
在這邊主要有幾個部分:
useState
:我們使用了三個狀態:
useCallback
:使用 useCallback
定義 fetchAPI 函數,確認不會每次 render 時重新建立。apiFunction
:這個 hook 需要傳入 API request 的 function。雖然 useApiRequest hook 提供了一種通用的方式來執行 API request,但我們仍然需要設定分別的 API endpoint request。
import useApiRequest from "@/api/base";
import apiClient from "@/api/client";
// get serverHost from env
const apiURL = `${process.env.NEXT_PUBLIC_API_HOST}/api`;
export const useGetChannels = () => {
const getChannels = async (options) => {
const response = await apiClient.GET(`${apiURL}/channels`, options);
return response.json();
};
return useApiRequest(getChannels);
};
export const useGetChannelById = () => {
const getChannelById = async (channelId, options) => {
const response = await apiClient.GET(`${apiURL}/channels/${channelId}`, options);
return response.json();
};
return useApiRequest(getChannelById);
};
export const useGetEpisodeById = () => {
const getEpisodeById = async (episodeId, options) => {
const response = await apiClient.GET(`${apiURL}/episodes/${episodeId}`, options);
return response.json();
};
return useApiRequest(getEpisodeById);
};
在這邊有幾個部分:
NEXT_PUBLIC_API_HOST
來自於環境變數,這樣可以預留出不同環境(如開發、測試、生產)去切換的靈活性。getChannelById
為例,會直接回傳 response 的 json。useGetChannelById
為例,把 getChannelById
再經過 useApiRequest
hook 的包裝。在需要的 page 中使用 API 資料只需要引入對應的 hook 就可以。
import {useGetChannels} from '@/api/all';
// ..
const {responseData, fetchAPI} = useGetChannels();
useEffect(() => {
if (channelSlug && channelSlug.length > 0) {
fetchAPI(responseData);
}
}, [channelSlug, fetchAPI]);
只需要簡單檢查一下 channelSlug
是否有效,就可以很簡單達成從 API 取得資料的流程。